package com.mmq.ets.util;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class ListeChainee<T> implements Iterable<T>, Cloneable {
//Je me suis inspiré, au tout début, d'une class trouvé sur le web, voici l'adresse:
//http://fr.openclassrooms.com/forum/sujet/liste-chaine-et-java-73026
// elle n'y ressemble que pour la classe interne, il n'y a aucun copié collé


	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}

	private Chainon premier;
	private Chainon dernier;
	private int iterat; //l'indice du nombre d'objet
	

	//class interne permettant de faire le liens entre un objet et le suivant
	private class Chainon{
		T donnee; //l'objet courant
		Chainon suivant; // l'objet suivant
		int ite; //sa position dans la liste chainée
		public Chainon(T donnee, Chainon suiv, int iteration)
		{
			this.donnee=donnee;
			this.suivant=suiv;
			this.ite=iteration;
		}
	}

	public ListeChainee(){
		premier=null;
		iterat=0;
	}
	/**
	 * Permet d'ajouter un objet a la liste chainée
	 * @param objet : L'objet qu'on désire ajouter.
	 */
	public void ajouter(T objet){
		Chainon temp= new Chainon(objet,null,iterat++); // On met un objet dans la chaine avec comme référence null pour le prochain objet
		
		if (premier!=null){ //si ce n'est pas le premier..
			dernier.suivant=temp;
		}
		else{
			premier=temp;
		}
		dernier=temp; //le dernier est le nouvel objet ajouter.
	}
	
	/**
	 * obtenir un objet de la liste chainée
	 * @param i : L'indice de l'objet désiré
	 * @return : L'objet du type x.
	 */
	public T get(int i){
		Chainon temp;
		
			if (premier.ite==i) //Si le premier convient à ce que l'utilisateur recherchait, on lui retourne l'objet
				return premier.donnee; 	
			temp=premier.suivant; //temp contient le deuxieme
			for (int j=1;j<iterat;j++){
				if (temp.ite==i)
					return temp.donnee;
				else
					temp=temp.suivant;
			}
			return null; //il ne se trouve pas dans la chaine
		
	}
	public void set(int indice,T element)
	{
		Chainon temp;
		if (premier.ite==indice)
		{ //Si le premier convient à ce que l'utilisateur recherchait, on lui retourne l'objet
			premier.donnee=element;
			return;
		}	
		temp=premier.suivant; //temp contient le deuxieme
		for (int j=1;j<iterat;j++){
			if (temp.ite==indice)
				temp.donnee=element;
			else
				temp=temp.suivant;
		}
	}
	
	/**
	 * Supprime le premier de la liste chainée
	 * @return l'objet qui a été supprimé (facultatif, mais intéressant à avoir)
	 */
	public T supprimerPremier(){
		if (premier==null)
			return null;
		else{
			Chainon c=premier;
			premier=premier.suivant; //On écrase le premier par le deuxieme.
			return c.donnee;
		}		
	}
	
	/**
	 * NON FONCTIONNEL
	 * @param indice: L'indice de l'objet que l'on veut retirer
	 * @return l'objet supprimer à l'indice passé en paramètre
	 */
	public T retirer(int indice){ //retiré a un indice précis **NON FONCTIONNEL, NEED HELP !?**
		Chainon temp=premier;
		
		if (premier.ite==indice) //Si le premier convient à ce que l'utilisateur recherchait, on lui retourne l'objet
			 supprimerPremier();
		else{
			
			for (int j=1;j<iterat;j++){
				if (premier.ite==indice){
					T elementSupp=premier.donnee;
					premier.suivant=premier; //on écrase la xIem par sa suivante
					premier=temp; //on remet la référence du premier;
					iterat--;
					return elementSupp;
				}
				else
					premier=premier.suivant;
			}
		}
		premier=temp;
		return null; //il ne se trouve pas dans la chaine
	}
	
	/**
	 * Supprimer toute la chaine
	 */
	public void toutSupprimer(){
		while(premier!=null){
			supprimerPremier();
		}
		iterat=0;
	}
	
	/**
	 * Renvoyer le dernier élément de la liste chainée
	 * @return l'objet T
	 */
	public T renvoyerDernier(){
		return dernier.donnee;
	}
	
	/**
	 * Indique le nombre d'objet dans la liste chainée
	 * @return
	 */
	public int taille() 
	{
		return iterat;
	}

	
	@Override
	public Iterator<T> iterator() {
		return new IterartorElements();
	}
	 public class IterartorElements implements Iterator<T>
	    {
	    	ListeChainee.Chainon premier=ListeChainee.this.premier;
	    	ListeChainee.Chainon temp;
	     	@Override
			public boolean hasNext() 
	     	{
				return premier!=null;
			}

			@Override
			public T next()
			{
				if (!hasNext()) throw new NoSuchElementException();
				temp=premier;
				remove();
	        	return (T) temp.donnee;
			}

			@Override
			public void remove() 
			{
			    premier=premier.suivant;
			}

				
		  }
	

}
